package la.iok.hzsvn.lewin.movie.services;

import la.iok.hzsvn.lewin.cloud.invoker.service.RestServiceInvoker;
import la.iok.hzsvn.lewin.movie.core.model.*;
import la.iok.hzsvn.lewin.movie.core.movieservice.vo.MovieLevelVo;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.NotNullElement;
import la.iok.hzsvn.share.annotations.Nullable;
import la.iok.hzsvn.share.protocol.exception.ErrorCodeException;
import la.iok.hzsvn.share.response.ListDataResponse;
import la.iok.hzsvn.share.response.OperatorResponse;
import la.iok.hzsvn.share.response.SingleDataResponse;
import la.iok.hzsvn.share.utils.CollectionUtils;
import la.iok.hzsvn.share.utils.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static la.iok.hzsvn.lewin.movie.core.exception.OperatorResponseParser.parseResponse;

public class MovieServiceInvokerImpl implements MovieServiceInvoker {
    private final RestServiceInvoker restServiceInvoker;

    public MovieServiceInvokerImpl(RestServiceInvoker restServiceInvoker) {
        this.restServiceInvoker = restServiceInvoker;
    }

    @Override
    public RoomVo roomInfo(Long id) {
        if(id == null){
            return null;
        }
        SingleDataResponse<RoomVo> res = restServiceInvoker.getForObject("userserver","/internal-api/rooms/{id}",ROOM_TYPE,id);
        return parseResponse(res);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<RoomVo> roomInfos(@Nullable Collection<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return Collections.emptyList();
        }
        ids = ids.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        if(ids.isEmpty()){
            return Collections.emptyList();
        }
        ListDataResponse<RoomVo> userRes = restServiceInvoker.getForObject("userserver", "/internal-api/rooms?ids={ids}",
                ROOM_RES_TYPE,String.join(",",ids.stream().map(String::valueOf).collect(Collectors.toSet())));
        List<RoomVo> list = parseResponse(userRes);
        return emptyIfNull(list);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<RoomVo> shopRooms(@Nullable Collection<Long> shopIds) {
        if(CollectionUtils.isEmpty(shopIds)){
            return Collections.emptyList();
        }
        shopIds = shopIds.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        if(shopIds.isEmpty()){
            return Collections.emptyList();
        }
        ListDataResponse<RoomVo> roomRes = restServiceInvoker.getForObject("userserver", "/internal-api/rooms/shopRooms?orgUnitIds={orgUnitIds}",
                ROOM_RES_TYPE,String.join(",",shopIds.stream().map(String::valueOf).collect(Collectors.toSet())));
        List<RoomVo> list = parseResponse(roomRes);
        return emptyIfNull(list);
    }

    @Override
    public DeviceVo deviceInfo(@Nullable Long id) {
        if(id == null){
            return null;
        }
        SingleDataResponse<DeviceVo> deviceRes = restServiceInvoker.getForObject("userserver",
                "/internal-api/devices/{id}", DEVICE_RES_TYPE,id);
        return parseResponse(deviceRes);
    }

    @Override
    public DeviceVo deviceInfo(@Nullable String deviceCode) {
        if(StringUtils.isBlank(deviceCode)){
            return null;
        }
        SingleDataResponse<DeviceVo> deviceRes = restServiceInvoker.getForObject("userserver",
                "/internal-api/devices/code/{deviceCode}", DEVICE_RES_TYPE,deviceCode);
        return parseResponse(deviceRes);
    }

    @Override
    public UserVo userInfo(Long id) {
        if(id == null){
            return null;
        }
        SingleDataResponse<UserVo> userRes = restServiceInvoker.getForObject("userserver", "/internal-api/users/{id}",
                USER_RES_TYPE,id);
        return parseResponse(userRes);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<SimpleUser> userInfo(@Nullable Collection<Long> ids) {
        if(CollectionUtils.isEmpty(ids)){
            return Collections.emptyList();
        }
        ids = ids.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        if(ids.isEmpty()){
            return Collections.emptyList();
        }
        ListDataResponse<SimpleUser> userRes = restServiceInvoker.getForObject("userserver", "/internal-api/users?ids={ids}",
                USER_LIST_RES_TYPE,String.join(",",ids.stream().map(String::valueOf).collect(Collectors.toSet())));
        List<SimpleUser> list = parseResponse(userRes);
        return emptyIfNull(list);
    }

    @Override
    public MovieVo movieInfo(Long movieId, String version) {
        if(movieId == null || StringUtils.isBlank(version)){
            return null;
        }
        SingleDataResponse<MovieVo> movieRes = restServiceInvoker.getForObject("movieserver",
                "/internal-api/movieInstances/movie/{movieId}/{version}",MOVIE_RES_TYPE
                ,movieId, version);
        return parseResponse(movieRes);
    }

    @Override
    @NotNull
    @NotNullElement
    public List<MovieVo> latestVersions(@Nullable Collection<Long> movieIds) {
        if(movieIds == null || movieIds.isEmpty()){
            return Collections.emptyList();
        }
        String ids = movieIds.stream().filter(Objects::nonNull).map(String::valueOf).collect(Collectors.joining(","));
        if(StringUtils.isBlank(ids)){
            return Collections.emptyList();
        }
        ListDataResponse<MovieVo> movies = restServiceInvoker.getForObject("movieserver",
                "/internal-api/movieInstances/latest?movieIds={movieIds}",MOVIE_LIST_RES_TYPE,ids);
        return emptyIfNull(parseResponse(movies));
    }

    @Nullable
    @Override
    public SimpleOrgUnitVo orgUnitInfo(@Nullable Long orgUnitId) {
        if(orgUnitId == null){
            return null;
        }
        SingleDataResponse<SimpleOrgUnitVo> orgUnit = restServiceInvoker.getForObject("userserver",
                "/internal-api/orgUnits/{id}/", ORG_UNIT_RES_TYPE, orgUnitId);
        return parseResponse(orgUnit);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<SimpleOrgUnitVo> orgUnits(@Nullable Collection<Long> orgUnitIds) {
        if(CollectionUtils.isEmpty(orgUnitIds)){
            return Collections.emptyList();
        }
        orgUnitIds = orgUnitIds.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        if(orgUnitIds.isEmpty()){
            return Collections.emptyList();
        }
        ListDataResponse<SimpleOrgUnitVo> response = restServiceInvoker.getForObject("userserver",
                "/internal-api/orgUnits?ids={ids}", ORG_UNIT_LIST_RES_TYPE, StringUtils.join(",",orgUnitIds.toArray()));
        List<SimpleOrgUnitVo> items = parseResponse(response);
        return emptyIfNull(items);
    }


    @NotNull
    @NotNullElement
    @Override
    public List<Long> childrenOrgUnitIds(Long orgUnitId) {
        if(orgUnitId == null){
            return Collections.emptyList();
        }
        ListDataResponse<Long> orgUnitIds = restServiceInvoker.getForObject("userserver",
                "/internal-api/orgUnits/{id}/childrenIds", LIST_LONG_RES_TYPE, orgUnitId);
        List<Long> ids = parseResponse(orgUnitIds);
        return emptyIfNull(ids);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<Long> childrenOrgUnitIds(@Nullable Collection<Long> orgUnitIds) {
        if(CollectionUtils.isEmpty(orgUnitIds)){
            return Collections.emptyList();
        }
        ListDataResponse<Long> response = restServiceInvoker.getForObject("userserver",
                "/internal-api/orgUnits/childrenIds?ids={ids}", LIST_LONG_RES_TYPE, StringUtils.join(",",orgUnitIds.toArray()));
        List<Long> ids = parseResponse(response);
        return emptyIfNull(ids);
    }

    @NotNull
    @NotNullElement
    @Override
    public List<FinancialAccountVo> roomAccounts(@Nullable Collection<Long> roomIds) {
        if(roomIds == null){
            return Collections.emptyList();
        }
        ListDataResponse<FinancialAccountVo> res = restServiceInvoker.getForObject("operation",
                "/internal-api/financialAccounts?roomId={roomId}", LIST_ACCOUNT_RES_TYPE,
                StringUtils.join(",",roomIds.toArray()));
        List<FinancialAccountVo> list = parseResponse(res);
        return emptyIfNull(list);
    }

    @Override
    public FinancialAccountVo roomAccount(Long roomId) {
        if(roomId == null){
            return null;
        }
        ListDataResponse<FinancialAccountVo> res = restServiceInvoker.getForObject("operation",
                "/internal-api/financialAccounts?roomId={roomId}",LIST_ACCOUNT_RES_TYPE, roomId);
        List<FinancialAccountVo> list = parseResponse(res);
        if(list == null || list.size() == 0){
            return null;
        }
        return list.get(0);
    }

    @Nullable
    @Override
    public PlayRecordVo playRecord(@Nullable Long id) {
        if(id == null){
            return null;
        }
        SingleDataResponse<PlayRecordVo> res = restServiceInvoker.getForObject("operation",
                "/internal-api/playRecords/{id}",PLAY_RECORD_RES_TYPE,id);
        return parseResponse(res);
    }

    @Override
    public List<PlayRecordVo> roomPlayingRecords(@Nullable Long roomId) {
        if(roomId == null){
            return Collections.emptyList();
        }
        ListDataResponse<PlayRecordVo> res = restServiceInvoker.getForObject("operation",
                "/internal-api/playRecords/playing?roomId={roomId}",PLAY_RECORD_LIST_RES_TYPE,roomId);
        List<PlayRecordVo> list = parseResponse(res);
        return emptyIfNull(list);
    }

    @Override
    public PlayRoleInstVo playRoleInst(@Nullable Long movieId,@Nullable String version,@Nullable Long playRoleId) {
        if(movieId == null || playRoleId == null || StringUtils.isBlank(version)){
            return null;
        }
        SingleDataResponse<PlayRoleInstVo> res = restServiceInvoker.getForObject("movieserver",
                "/internal-api/playRoleInstances/movie/{movieId}/v/{version}/playRole/{id}",PLAY_ROLE_INST_TYPE
                ,movieId, version,playRoleId);
        return parseResponse(res);
    }

    @Override
    public void increasePlayTimes(Long movieId, String version) {
        OperatorResponse res = restServiceInvoker.putForObject("movieserver",
                "/internal-api/movieInstances/movie/{movieId}/{version}/playTimes",null,
                OperatorResponse.class,movieId,version);
        if(!res.success()){
            throw new ErrorCodeException(res.getCode(),null,res.getMessage());
        }
    }

    @Override
    public List<UserRoleDetail> userRoles(@Nullable Long userId) {
        if(userId == null){
            return Collections.emptyList();
        }
        ListDataResponse<UserRoleDetail> res = restServiceInvoker.getForObject("userserver",
                "/userRoles/user/{userId}", USER_ROLE_RES_TYPE,userId);
        List<UserRoleDetail> list = parseResponse(res);
        return emptyIfNull(list);
    }

    @Override
    public List<Long> unsupportedReturnMoneyMovieIds() {
        ListDataResponse<Long> response = restServiceInvoker.getForObject("movieserver","/internal-api/movies/unsupportedReturnMoney",LIST_LONG_RES_TYPE);
        return emptyIfNull(parseResponse(response));
    }

    @Override
    public List<MovieLevelVo> movieLevels() {
        ListDataResponse<MovieLevelVo> response = restServiceInvoker.getForObject("movieserver","/internal-api/movieLevels",MOVIE_LEVEL_RES_TYPE);
        return emptyIfNull(parseResponse(response));
    }

    private <T> List<T> emptyIfNull(List<T> coll){
        if(coll == null){
            return Collections.emptyList();
        }
        return coll.stream().filter(Objects::nonNull).collect(Collectors.toList());
    }
}
